const library = require("../library/index.js");


module.exports = function(lab) {
    return {
        @saveIdentity: function({ id, name, symbol, parent_id}) {
            
            return lab.snapshot(function(snapshot) {

                return @.async(function() {

                    if (id) {
                        snapshot.find("backend_identities", id).pipe(this);
                    } else {
                        this.next();
                    }

                }).then(function(existing_identity) {

                    this.pool.existing_identity = existing_identity;

                    if (parent_id) {

                        snapshot.find("backend_identities", parent_id).then(function(identity) {

                            if (identity) {
                                this.next(identity);
                            } else {
                                throw @lab.errors.object_not_found;
                            }

                        }).pipe(this);

                    } else {
                        this.next();
                    }

                }).then(function(parent_identity) {

                    this.pool.parent_identity = parent_identity;

                    if (this.pool.existing_identity) {

                        if ((!parent_identity && this.pool.existing_identity.parent_id) || (parent_identity && this.pool.existing_identity.parent_id != this.pool.existing_identity.parent_id)){
                            throw @lab.errors.object_not_found;
                        }

                        snapshot.update("backend_identities", this.pool.existing_identity, {
                            name  : name,
                            symbol : symbol
                        }).pipe(this);

                    } else {

                        @.async(function(){

                            snapshot.sqlite.query("select max(idx) as idx from backend_identities").then(function(result){
                                if (result.records.length) {
                                    this.next(result.records[0].idx);
                                } else {
                                    this.next(0);
                                }
                            }).pipe(this);

                        }).then(function(idx){

                            snapshot.create("backend_identities", {
                                id   : id,
                                name : name,
                                symbol : symbol,
                                parent_id : parent_identity ? parent_identity.id : null,
                                idx : idx + 1,
                                parent_idx : parent_identity ? parent_identity.idx : null,
                                hash : (parent_identity ? parent_identity.hash : 0) | (1 << (idx + 1))
                            }).pipe(this);

                        }).pipe(this);
                    }

                });
            })
        },

        @list : function(ids){

            if (ids && Array.isArray(ids) && ids.length) {

                return lab.sqlite.find("backend_identities",{
                    "id" : ["in", ids]
                }).then(function(result){
                    this.next(result.records);
                });

            } else {

                return @.async.resolve([]);

            }
        },
        @getIdentities : function({uid, user}) {

            return @.async(function(){

                if (user) {

                    this.next(user);

                } else if (uid){

                    lab.find("backend_operators", uid).then(function(user){
                        if (found_user) {
                            this.next(found_user);
                        } else {
                            throw @lab.errors.object_not_found;
                        }
                    }).pipe(this);

                } else {
                    throw @lab.errors.object_not_found;
                }
                

            }).then(function(the_user){
                
                if (the_user.identity == 0) {
                    this.next([]);
                } else {
                    lab.sqlite.find("backend_identities",{
                        "!" : ["hash & ? <> 0", the_user.identity]
                    }).then(function(result){
                        if (result.records.length){
                            this.next(result.records);
                        } else {
                            this.next();
                        }
                    }).pipe(this);
                }

            });
        },

        @change: function({ identity, identity_id , uid, user , operator_id, operator, action }) {
            
            return lab.snapshot(function(snapshot){
                return @.async(function(){

                    if (user) {
    
                        this.next(user);
    
                    } else if (uid){
                        
                        snapshot.find("backend_operators", uid).then(function(found_user){

                            if (found_user) {
                                this.next(found_user);
                            } else {
                                throw @lab.errors.object_not_found;
                            }

                        }).pipe(this);
    
                    } else {
                        throw @lab.errors.object_not_found;
                    }
                    
    
                }).then(function(user){
    
                    this.pool.user = user;
    
                    if (operator) {
    
                        this.next(operator);
    
                    } else if (operator_id){
    
                        snapshot.find("backend_operators", operator_id).then(function(found_operator){
                            if (found_operator) {
                                this.next(found_operator);
                            } else {
                                throw @lab.errors.object_not_found;
                            }
                        }).pipe(this);
    
                    } else {
                        this.next();
                    }
    
                }).then(function(operator){
    
                    this.pool.operator = operator;
    
                    if (identity) {
    
                        this.next(identity);
    
                    } else if (identity_id) {
    
                        snapshot.find("backend_identities", identity_id).then(function(identity) {
    
                            if (identity) {
                                this.next(identity);
                            } else {
                                throw @lab.errors.object_not_found;
                            }
        
                        }).pipe(this);
    
                    } else {
                        throw @lab.errors.object_not_found;
                    }
    
                    
    
                }).then(function(the_identity){
                    
                    var user = this.pool.user;
                    var operator = this.pool.operator;
                    
                    switch (action) {
                        case "grant" : {
                            user.identity = user.identity  | (1 << the_identity.idx);
                            break;
                        }
                        case "revoke" : {
                            user.identity = user.identity  & (~(1 << the_identity.idx));
                            break;
                        }
                        default : {
                            throw @lab.erros.invalid_parameters;
                        }
                    }

                    @.async(function(){
    
                        snapshot.update("backend_operators", user).pipe(this);
    
                    }).then(function(updated_user){

                        snapshot.create("backend_identity_operate_records",{
                            uid : user.id,
                            identity_id : the_identity.id,
                            action  : action,
                            create_date : Date.now(),
                            operator_id : operator ? operator.id : null
                        }).resolve(updated_user).pipe(this);
    
                    }).pipe(this);
    
                })
            })
            
        },

    }
}